#ifndef EVENTLOOP_HPP
#define EVENTLOOP_HPP

#include "Acceptor.hpp"
#include "TcpConnection.hpp"  // 含有头文件<memory>
#include <sys/eventfd.h>
#include <sys/epoll.h>
#include <vector>
#include <unordered_map>
#include <mutex>

using std::vector;
using std::unordered_map;
using std::mutex;
using std::lock_guard;

using Functor = std::function<void()>;

class EventLoop{ 
    // 使得子线程能通过TcpConnection对象调用本类的runInLoop，从而传递send()任务给主线程
    friend void TcpConnection::sendInLoop(const string &msg);  
public:
    EventLoop(Acceptor & acceptor);  // 构造函数, 需绑定一个Acceptor对象
    ~EventLoop();
    void loop();                     // 开启epoll事件循环
    void unloop();                   // 结束epoll事件循环
    void setNewConnectionCallback(TcpConnectionCallback && cb);
    void setMessageCallback(TcpConnectionCallback && cb);
    void setCloseCallback(TcpConnectionCallback && cb);

private:
    int createEpollFd();             // 创建epoll实例
    void addFdToEpoll(int fd);       // 将fd添加到epoll实例中
    void delFdFromEpoll(int fd);     // 从epoll实例中删除fd
    void waitEpollFd();              // 等待epoll实例中的fd就绪
    void handleNewConnection();      // 处理新连接
    void handleMessage(int fd);      // 处理已连接的客户端的消息

    // 与线程池子线程交互（子线程为生产者，主线程为消费者）
    int createEventFd();             // 创建eventfd文件描述符
    void handleRead();               // 读取eventfd文件描述符（封装read操作）(主线程调用)
    void wakeup();                   // 唤醒eventfd文件描述符（封装write操作）（在runInLoop中调用）
    void doPendingFunctors();        // 执行待处理的任务
    void runInLoop(Functor && cb);   // 存放任务到_pendings中，并且唤醒。（子线程通过TcpConnection间接调用）

private:
    int _epfd;
    vector<struct epoll_event> _evtList;  // 监听就绪队列
    bool _isLooping;                      // 事件循环是否正在运行
    Acceptor & _acceptor;                 // 绑定的Acceptor对象
    unordered_map<int, shared_ptr<TcpConnection>> _conns;  // 已连接的客户端的连接对象映射 fd --> TcpConnection*
    
    // 由用户指定对三个事件的处理方式（回调）
    // 先将三个回调交给EventLoop，然后由EventLoop传递给TcpConnection对象
    TcpConnectionCallback _onNewConnection; // 处理新建连接（信息打印）
    TcpConnectionCallback _onMessage;       // 处理客户端到达消息
    TcpConnectionCallback _onClose;         // 处理客户端连接关闭（信息打印）

    // 与线程池交互（子线程为生产者，主线程为消费者）
    int _evtfd;   // 进程间通信用的特殊文件描述符，用于线程池通知主线程
    vector<Functor> _pendings;  // 任务队列：存放来自子线程，待主线程处理的"send()"任务
    mutex _mtx;  // 互斥锁，保护_pendings的线程安全
};


#endif // EVENTLOOP_HPP
